🏠

Chapter 1: Why React? (And Why You Might Not Need It)

The JavaScript framework landscape in 2025

The JavaScript Framework Landscape in 2025

Before we dive into React, let's acknowledge the elephant in the room: the JavaScript ecosystem is overwhelming. New frameworks appear monthly, each claiming to solve problems you didn't know you had. By the time you finish reading this chapter, three new meta-frameworks will have launched on Twitter.

But here's the reality: the framework wars are over, and React won. Not because it's perfect—it's not. Not because it's the fastest—it isn't. React won because it became the default choice, and defaults are powerful.

The Current State of Play

As of 2025, the frontend landscape has consolidated around a few major players:

React (2013-present): The incumbent. 40%+ of all frontend jobs. Massive ecosystem. Backed by Meta. You're reading this book because React is the safe, pragmatic choice for most projects.

Vue (2014-present): The elegant alternative. Easier learning curve than React. Popular in Asia and among solo developers. Excellent documentation. Smaller job market.

Svelte (2016-present): The compiler-based upstart. No virtual DOM. Writes less code. Growing rapidly but still niche. SvelteKit (its Next.js equivalent) is maturing.

Angular (2016-present): The enterprise framework. TypeScript-first. Opinionated. Declining but still dominant in large corporations and government projects.

Solid (2021-present): The performance obsessive. React-like syntax but fundamentally different reactivity model. Tiny but passionate community.

Qwik (2021-present): The resumability pioneer. Focuses on instant interactivity. Interesting ideas, uncertain future.

What This Means for You

If you're learning frontend development in 2025, React is the most pragmatic choice for these reasons:

  1. Job market dominance: 4 out of 10 frontend job postings require React
  2. Ecosystem maturity: Every problem you'll face has a React solution
  3. Knowledge transferability: React patterns appear in other frameworks
  4. Next.js: The killer app that makes React viable for production
  5. Community size: More tutorials, more Stack Overflow answers, more help

But—and this is important—React is not always the right choice. We'll explore when to choose alternatives in section 1.3.

The Meta-Framework Era

Here's what changed between 2020 and 2025: nobody uses React alone anymore. The framework landscape has split into two layers:

Base frameworks (React, Vue, Svelte): Provide component models and reactivity Meta-frameworks (Next.js, Remix, Astro, Nuxt, SvelteKit): Add routing, data fetching, server rendering, and deployment

React without a meta-framework is like a car engine without a chassis. Technically functional, but you're not going anywhere comfortable. This book teaches React fundamentals first (Chapters 1-15), then Next.js (Chapters 16-22), because you need to understand the engine before you can drive the car.

The Hype Cycle Reality Check

Every few months, a new framework launches with bold claims:

Some of these claims are true. Most are irrelevant to your actual project constraints. Here's what matters more than raw performance:

  1. Can you hire developers who know it?
  2. Will it still be maintained in 3 years?
  3. Does it have libraries for your use case?
  4. Can you deploy it easily?
  5. Will your team understand the code in 6 months?

React scores well on all five questions. The new hotness often scores well on none.

Why This Book Focuses on React + Next.js

This book makes an opinionated choice: React with Next.js is the most pragmatic path to building production applications in 2025. Not the most exciting. Not the most innovative. The most pragmatic.

We'll teach you: - React fundamentals that transfer to other frameworks - TypeScript integration that makes large codebases maintainable - Next.js patterns that solve real production problems - When to reach for libraries vs. building yourself - How to debug when things go wrong (they will)

By the end, you'll understand not just how to use React, but when to use it—and when to walk away.

What React actually solves (and what it doesn't)

What React Actually Solves (and What It Doesn't)

Let's start with a concrete problem. Imagine you're building a simple counter interface. Here's how you'd do it with vanilla JavaScript:

<!DOCTYPE html>
<html>
<head>
  <title>Vanilla Counter</title>
</head>
<body>
  <div id="app">
    <h1>Count: <span id="count">0</span></h1>
    <button id="increment">Increment</button>
    <button id="decrement">Decrement</button>
    <button id="reset">Reset</button>
  </div>

  <script>
    let count = 0;
    const countElement = document.getElementById('count');
    const incrementButton = document.getElementById('increment');
    const decrementButton = document.getElementById('decrement');
    const resetButton = document.getElementById('reset');

    function updateDisplay() {
      countElement.textContent = count;
    }

    incrementButton.addEventListener('click', () => {
      count++;
      updateDisplay();
    });

    decrementButton.addEventListener('click', () => {
      count--;
      updateDisplay();
    });

    resetButton.addEventListener('click', () => {
      count = 0;
      updateDisplay();
    });
  </script>
</body>
</html>

This works perfectly. It's 30 lines of straightforward code. No build tools, no dependencies, no framework. You can open this file directly in a browser.

Now let's add a feature: display the count in three different places (header, sidebar, footer). And make the count turn red when negative, green when positive.

<!DOCTYPE html>
<html>
<head>
  <title>Vanilla Counter - Extended</title>
  <style>
    .positive { color: green; }
    .negative { color: red; }
    .zero { color: black; }
  </style>
</head>
<body>
  <header>
    <h1>Count: <span id="count-header" class="zero">0</span></h1>
  </header>

  <main>
    <div id="app">
      <button id="increment">Increment</button>
      <button id="decrement">Decrement</button>
      <button id="reset">Reset</button>
    </div>
  </main>

  <aside>
    <p>Sidebar count: <span id="count-sidebar" class="zero">0</span></p>
  </aside>

  <footer>
    <p>Footer count: <span id="count-footer" class="zero">0</span></p>
  </footer>

  <script>
    let count = 0;
    const countHeader = document.getElementById('count-header');
    const countSidebar = document.getElementById('count-sidebar');
    const countFooter = document.getElementById('count-footer');
    const incrementButton = document.getElementById('increment');
    const decrementButton = document.getElementById('decrement');
    const resetButton = document.getElementById('reset');

    function getColorClass(value) {
      if (value > 0) return 'positive';
      if (value < 0) return 'negative';
      return 'zero';
    }

    function updateDisplay() {
      const colorClass = getColorClass(count);
      const countText = count.toString();

      // Update header
      countHeader.textContent = countText;
      countHeader.className = colorClass;

      // Update sidebar
      countSidebar.textContent = countText;
      countSidebar.className = colorClass;

      // Update footer
      countFooter.textContent = countText;
      countFooter.className = colorClass;
    }

    incrementButton.addEventListener('click', () => {
      count++;
      updateDisplay();
    });

    decrementButton.addEventListener('click', () => {
      count--;
      updateDisplay();
    });

    resetButton.addEventListener('click', () => {
      count = 0;
      updateDisplay();
    });
  </script>
</body>
</html>

Now we're at 70 lines. Notice what happened:

  1. Manual DOM synchronization: We have to manually update three separate elements
  2. Repetitive code: The same update logic repeated three times
  3. Fragile coupling: If we add a fourth display location, we must remember to update updateDisplay()
  4. Error-prone: Forget to update one element, and your UI is inconsistent

This is the problem React solves: keeping the UI in sync with application state.

The React Solution

Here's the same functionality in React:

// Counter.tsx
import { useState } from 'react';

function Counter() {
  const [count, setCount] = useState(0);

  const colorClass = count > 0 ? 'positive' : count < 0 ? 'negative' : 'zero';

  return (
    <>
      <header>
        <h1>Count: <span className={colorClass}>{count}</span></h1>
      </header>

      <main>
        <button onClick={() => setCount(count + 1)}>Increment</button>
        <button onClick={() => setCount(count - 1)}>Decrement</button>
        <button onClick={() => setCount(0)}>Reset</button>
      </main>

      <aside>
        <p>Sidebar count: <span className={colorClass}>{count}</span></p>
      </aside>

      <footer>
        <p>Footer count: <span className={colorClass}>{count}</span></p>
      </footer>
    </>
  );
}

export default Counter;

This is 30 lines—same as our original vanilla version—but it handles the complex case. Notice what changed:

  1. No manual DOM updates: React handles synchronization automatically
  2. Single source of truth: count is defined once, used everywhere
  3. Declarative: We describe what the UI should look like, not how to update it
  4. Composable: Easy to extract <CountDisplay value={count} /> as a reusable component

What React Actually Solves

React solves one specific problem: keeping the DOM in sync with application state as that state changes over time.

It does this through:

  1. Declarative UI: You describe what the UI should look like for any given state
  2. Automatic updates: React figures out what changed and updates only what's necessary
  3. Component composition: Break complex UIs into reusable pieces
  4. Unidirectional data flow: Data flows down, events flow up (we'll explore this in Chapter 2)

What React Doesn't Solve

React is not a complete application framework. It doesn't provide:

  1. Routing: How to handle /products vs. /products/123 (you need React Router or Next.js)
  2. Data fetching: How to load data from APIs (you need fetch + useEffect, or React Query, or Next.js)
  3. State management: How to share state across distant components (you need Context, Zustand, or similar)
  4. Server rendering: How to render on the server for SEO and performance (you need Next.js or Remix)
  5. Build tooling: How to bundle, transpile, and optimize code (you need Vite, Next.js, or similar)
  6. Styling: How to style components (you need CSS, Tailwind, CSS-in-JS, or CSS Modules)
  7. Forms: How to handle complex form validation (you need React Hook Form or similar)
  8. Authentication: How to handle login/logout (you need NextAuth or similar)

This is why we said earlier: nobody uses React alone. You always need additional tools. This book will teach you the pragmatic stack:

The Mental Model Shift

The hardest part of learning React isn't the syntax—it's the mental model shift from imperative to declarative programming.

Imperative (vanilla JavaScript): 1. Get the element 2. Update its text content 3. Update its class name 4. Repeat for each element

Declarative (React): 1. Describe what the UI should look like given the current state 2. React handles the updates

This shift feels unnatural at first. Your brain wants to reach for document.getElementById(). Resist that urge. Trust that React will handle the updates. By Chapter 3, this will feel natural.

When Vanilla JavaScript Is Better

React adds complexity. Sometimes that complexity isn't justified. Use vanilla JavaScript when:

  1. The page is mostly static: A blog post with one interactive widget doesn't need React
  2. You're adding interactivity to an existing site: Sprinkling in React components is possible but awkward
  3. The interaction is simple: A single dropdown menu doesn't need a framework
  4. Performance is critical and the page is small: React's runtime has overhead (though it's small)
  5. You're building a browser extension: Smaller bundle size matters

Use React when:

  1. The UI has complex state: Multiple pieces of state that affect multiple parts of the UI
  2. You're building a single-page application: The entire page is dynamic
  3. You need component reusability: Building a design system or component library
  4. The team is already using React: Consistency matters more than theoretical purity
  5. You need server rendering: Next.js makes this trivial

The Honest Truth About React

React is not magic. It's not even particularly innovative anymore—the ideas are 10+ years old. But it's:

Is it the best framework? Depends on your definition of "best." Is it the most pragmatic choice for most projects in 2025? Yes.

Let's move on to when you should choose React vs. alternatives.

When to use React vs. alternatives

When to Use React vs. Alternatives

The framework you choose matters less than you think—and more than you'd hope. Let's cut through the hype and make pragmatic decisions.

The Decision Framework

When choosing a frontend framework, evaluate these factors in order:

  1. Team expertise: What does your team already know?
  2. Hiring constraints: Can you hire developers who know this framework?
  3. Project requirements: What are you actually building?
  4. Ecosystem needs: Do you need specific libraries?
  5. Performance requirements: Are you building a content site or an app?
  6. Deployment constraints: Where will this run?

Notice that "framework performance benchmarks" isn't on the list. That's intentional. Micro-benchmarks rarely matter in real applications.

When to Choose React

Choose React when:

You're building a complex, interactive application - Dashboard with real-time updates - Admin panel with many forms - Social media feed - Collaborative editing tool - E-commerce site with dynamic filtering

You need to hire developers - React has the largest talent pool - Easier to find senior developers - More bootcamp graduates know React - Lower training costs for new hires

You're working with an existing React codebase - Consistency matters more than theoretical purity - Rewriting is almost never worth it - Incremental improvements are safer

You need a mature ecosystem - Every problem has a React solution - Component libraries (shadcn/ui, Radix, Chakra) - State management (Zustand, Redux, Jotai) - Data fetching (React Query, SWR) - Forms (React Hook Form, Formik)

You want the Next.js advantage - Server rendering without complexity - File-based routing - API routes - Image optimization - Deployment to Vercel (one command)

When to Choose Vue

Choose Vue when:

You're a solo developer or small team - Easier learning curve than React - Less boilerplate - Better developer experience out of the box - Single-file components are intuitive

You're building a content-heavy site with some interactivity - Marketing site with interactive demos - Documentation site with live examples - Blog with dynamic features

You value official tooling - Vue CLI is excellent - Vite was created by Vue's author - Vue DevTools are polished - Official router and state management

You're in Asia or Europe - Larger Vue communities in these regions - More local job opportunities - Better local language documentation

When to Choose Svelte

Choose Svelte when:

You're building a small to medium application - Svelte's compiler approach shines here - Less runtime overhead - Smaller bundle sizes

You value developer experience - Less boilerplate than React - More intuitive reactivity - Cleaner syntax - Built-in animations

You're okay with a smaller ecosystem - Fewer libraries available - Smaller community - Less Stack Overflow answers - But SvelteKit is maturing rapidly

Performance is critical - No virtual DOM overhead - Smaller bundle sizes - Faster initial load

When to Choose Angular

Choose Angular when:

You're in a large enterprise - Angular is popular in corporations - Opinionated structure helps large teams - TypeScript-first from day one - Comprehensive official tooling

You need everything in one box - Routing, state management, HTTP client all included - No decision fatigue - Consistent patterns enforced

You're building a long-lived application - Angular's stability is excellent - Breaking changes are rare - Long-term support is guaranteed

You have Java/C# developers - Angular's patterns feel familiar - Dependency injection - Decorators and classes - Strong typing

When to Choose Vanilla JavaScript

Choose vanilla JavaScript when:

You're adding one interactive widget to a static site - A single dropdown menu - An image carousel - A simple form - A modal dialog

The page is mostly static - Blog posts - Marketing pages - Documentation - Landing pages

You're building a browser extension - Bundle size matters - No build step is simpler - Faster development iteration

You're learning web fundamentals - Understanding the DOM is crucial - Framework knowledge builds on vanilla JS - You'll debug frameworks better

The Pragmatic Reality

Here's what actually happens in most projects:

  1. You inherit an existing codebase: Use whatever it's already using
  2. Your company has a standard: Use that standard
  3. You're starting fresh: Choose React + Next.js unless you have a specific reason not to
  4. You're building a side project: Choose whatever you want to learn

The "Just Use React" Heuristic

If you're unsure, default to React + Next.js because:

  1. Lowest risk: Most likely to still be maintained in 5 years
  2. Easiest hiring: Largest talent pool
  3. Best ecosystem: Most libraries and tools
  4. Proven at scale: Used by Meta, Netflix, Airbnb, etc.
  5. Next.js is excellent: Solves most production problems

Is it the most exciting choice? No. Is it the most pragmatic? Yes.

When NOT to Use React

Don't use React when:

You're building a static site - Use Astro, 11ty, or Hugo - Ship zero JavaScript by default - Add interactivity with islands

You need instant interactivity - Use Qwik or Astro with islands - React's hydration has overhead - Content sites suffer most

You're building a mobile app - Use React Native (different from React) - Or use native Swift/Kotlin - Web views are a last resort

You're building a game - Use a game engine (Unity, Godot) - Or vanilla Canvas/WebGL - React's overhead hurts here

You're building a data visualization - Use D3.js directly - Or Observable Plot - React adds unnecessary layers

The Migration Path

If you choose wrong, you can migrate:

React → Next.js: Trivial, Next.js is React React → Vue: Moderate difficulty, similar concepts React → Svelte: Moderate difficulty, different reactivity Vue → React: Moderate difficulty, similar concepts Angular → React: High difficulty, very different patterns Vanilla → React: Easy, incremental adoption possible

The hardest migrations are away from Angular and between fundamentally different paradigms (e.g., React to Solid).

Decision Matrix

Here's a quick reference:

Project Type Recommended Alternative Avoid
SPA Dashboard React + Next.js Vue + Nuxt Angular (overkill)
Marketing Site Astro Next.js React alone
E-commerce Next.js Remix Vanilla JS
Admin Panel React + Next.js Vue + Nuxt Svelte (ecosystem)
Blog Astro Next.js React alone
Documentation Astro Next.js Angular
Mobile App React Native Flutter React (wrong tool)
Browser Extension Vanilla JS Svelte React (bundle size)

The Bottom Line

For most web applications in 2025, React + Next.js is the pragmatic default. Not because it's perfect, but because it's:

Choose something else when you have a specific reason, not just because it's newer or claims to be faster.

Now let's actually build something.

Setting up your first React project with Vite

Setting up Your First React Project with Vite

Theory is useful. Working code is better. Let's build something.

Why Vite?

Before we start, let's address the elephant: why Vite and not Create React App (CRA)?

Create React App was the standard way to start React projects from 2016-2023. As of 2025, it's effectively deprecated:

Vite (pronounced "veet", French for "fast") is the modern replacement:

The React team now recommends using a framework (Next.js, Remix) or Vite for new projects. We'll use Vite for learning React fundamentals (Chapters 1-15), then switch to Next.js for production patterns (Chapters 16-22).

Prerequisites

Before we begin, ensure you have:

  1. Node.js 18+: Check with node --version
  2. npm 9+: Check with npm --version
  3. A code editor: VS Code is recommended (see Appendix A for setup)
  4. A modern browser: Chrome, Firefox, or Edge with DevTools

If you don't have Node.js, download it from nodejs.org. The LTS (Long Term Support) version is recommended.

Creating Your First React Project

Open your terminal and run:

npm create vite@latest my-first-react-app -- --template react-ts

Let's break down this command:

You'll see output like this:

Scaffolding project in /Users/you/my-first-react-app...

Done. Now run:

  cd my-first-react-app
  npm install
  npm run dev

Follow those instructions:

cd my-first-react-app
npm install
npm run dev

After a few seconds, you'll see:

VITE v5.0.0  ready in 234 ms

  âžś  Local:   http://localhost:5173/
  âžś  Network: use --host to expose
  âžś  press h + enter to show help

Open your browser to http://localhost:5173/. You should see the Vite + React welcome page with a counter button.

Congratulations! You just created your first React application. Let's understand what just happened.

Project Structure

Open the project in your code editor. You'll see this structure:

my-first-react-app/
├── node_modules/          # Dependencies (don't touch)
├── public/                # Static assets
│   └── vite.svg
├── src/                   # Your application code
│   ├── assets/
│   │   └── react.svg
│   ├── App.css           # Component styles
│   ├── App.tsx           # Main component
│   ├── index.css         # Global styles
│   └── main.tsx          # Application entry point
├── .gitignore            # Git ignore rules
├── index.html            # HTML entry point
├── package.json          # Project metadata and dependencies
├── tsconfig.json         # TypeScript configuration
├── tsconfig.node.json    # TypeScript config for Node.js
└── vite.config.ts        # Vite configuration

Let's examine the key files:

The Entry Point: index.html

Open index.html:

<!doctype html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <link rel="icon" type="image/svg+xml" href="/vite.svg" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Vite + React + TS</title>
  </head>
  <body>
    <div id="root"></div>
    <script type="module" src="/src/main.tsx"></script>
  </body>
</html>

Notice two critical lines:

  1. <div id="root"></div>: This is where React will render your application
  2. <script type="module" src="/src/main.tsx"></script>: This loads your React code

The React Entry Point: src/main.tsx

Open src/main.tsx:

import React from 'react'
import ReactDOM from 'react-dom/client'
import App from './App.tsx'
import './index.css'

ReactDOM.createRoot(document.getElementById('root')!).render(
  <React.StrictMode>
    <App />
  </React.StrictMode>,
)

This file does three things:

  1. Imports React and ReactDOM: The core libraries
  2. Imports your App component: The root of your component tree
  3. Renders the App: Mounts it to the #root div in index.html

The React.StrictMode wrapper enables additional development checks. We'll discuss this in Chapter 3.

Your First Component: src/App.tsx

Open src/App.tsx:

import { useState } from 'react'
import reactLogo from './assets/react.svg'
import viteLogo from '/vite.svg'
import './App.css'

function App() {
  const [count, setCount] = useState(0)

  return (
    <>
      <div>
        <a href="https://vitejs.dev" target="_blank">
          <img src={viteLogo} className="logo" alt="Vite logo" />
        </a>
        <a href="https://react.dev" target="_blank">
          <img src={reactLogo} className="logo react" alt="React logo" />
        </a>
      </div>
      <h1>Vite + React</h1>
      <div className="card">
        <button onClick={() => setCount((count) => count + 1)}>
          count is {count}
        </button>
        <p>
          Edit src/App.tsx

This is a React component. Don't worry about understanding every line yet—we'll cover components in depth in Chapter 2. For now, notice:

  1. It's a function: function App() { ... }
  2. It uses state: const [count, setCount] = useState(0)
  3. It returns JSX: HTML-like syntax that describes the UI
  4. It's exported: export default App so other files can import it

Making Your First Change

Let's modify the component to make it yours. Replace the entire contents of src/App.tsx with:

import { useState } from 'react'
import './App.css'

function App() {
  const [count, setCount] = useState(0)

  return (
    <div className="App">
      <h1>My First React App</h1>
      <p>This is a counter. Click the button to increment it.</p>
      <button onClick={() => setCount(count + 1)}>
        Count: {count}
      </button>
      <button onClick={() => setCount(0)}>
        Reset
      </button>
    </div>
  )
}

export default App

Save the file. Your browser should automatically update—this is Hot Module Replacement (HMR), one of Vite's killer features. You don't need to manually refresh.

Understanding What You Just Built

Let's break down what's happening:

  1. State declaration: const [count, setCount] = useState(0)
  2. Creates a piece of state called count, initialized to 0
  3. setCount is a function to update that state
  4. We'll explore this deeply in Chapter 3

  5. Event handler: onClick={() => setCount(count + 1)}

  6. When the button is clicked, call setCount with count + 1
  7. React re-renders the component with the new count
  8. The UI updates automatically

  9. JSX: The HTML-like syntax

  10. It's not HTML—it's JavaScript that looks like HTML
  11. Gets compiled to React.createElement() calls
  12. We'll explore JSX in Chapter 2

The Development Workflow

Your development workflow will be:

  1. Edit code in your editor
  2. Save the file
  3. See changes instantly in the browser (HMR)
  4. Check the console for errors (we'll set this up next)

No manual refresh needed. No build step during development. This is why Vite is fast.

Setting Up Your Diagnostic Toolkit

Before we go further, let's set up the tools you'll use to debug React applications. These tools are essential—you'll use them in every chapter.

Browser DevTools

Open your browser's DevTools:

You should see several tabs: Elements, Console, Sources, Network, etc.

The Console Tab

Click the Console tab. This is where you'll see:

Try adding a console.log to your component:

import { useState } from 'react'
import './App.css'

function App() {
  const [count, setCount] = useState(0)

  console.log('App rendered with count:', count)

  return (
    <div className="App">
      <h1>My First React App</h1>
      <p>This is a counter. Click the button to increment it.</p>
      <button onClick={() => setCount(count + 1)}>
        Count: {count}
      </button>
      <button onClick={() => setCount(0)}>
        Reset
      </button>
    </div>
  )
}

export default App

Save and check the console. You should see:

App rendered with count: 0

Click the increment button. You'll see:

App rendered with count: 0
App rendered with count: 1

This is your first insight into how React works: Every time state changes, the component re-renders. The console.log runs on every render. We'll explore this deeply in Chapter 3.

React Developer Tools

The browser's built-in DevTools are good, but React has its own specialized tools. Install the React Developer Tools extension:

After installing, refresh your React app. You'll see two new tabs in DevTools:

  1. ⚛️ Components: Inspect the React component tree
  2. ⚛️ Profiler: Measure component performance

Using React DevTools - Components Tab

Click the ⚛️ Components tab. You'll see your component tree:

â–Ľ App
    <div.App>
      <h1>
      <p>
      <button>
      <button>

Click on App in the tree. On the right side, you'll see:

Props: (empty for now—we'll cover props in Chapter 2)

Hooks: - State: 0 (your count state)

Try clicking the increment button in your app. Watch the Hooks section in React DevTools. You'll see the state value update in real-time.

This is your second critical insight: React DevTools shows you the internal state of your components. When debugging, you'll spend a lot of time here.

Using React DevTools - Profiler Tab

Click the ⚛️ Profiler tab. Click the blue record button (circle), then click your increment button a few times, then click stop (square).

You'll see a flame graph showing: - Which components rendered - How long each render took - Why each component rendered

For now, you'll see App rendered multiple times, each taking ~0.1ms. This is normal. We'll use the Profiler extensively in Chapter 25 (Performance Optimization).

The Network Tab

Click the Network tab in your browser's DevTools. Refresh the page. You'll see all the files your app loads:

This tab is crucial for debugging data fetching issues. We'll use it extensively in Chapter 4 (Data Fetching) and Chapter 18 (Next.js Data Fetching).

Your Complete Diagnostic Toolkit

You now have four essential tools:

  1. Browser Console: Errors, warnings, and logs
  2. React DevTools - Components: Component tree and state inspection
  3. React DevTools - Profiler: Performance measurement
  4. Network Tab: HTTP requests and responses

Memorize these keyboard shortcuts:

Verifying Your Setup

Let's verify everything works. Create a new file src/Greeting.tsx:

// src/Greeting.tsx
interface GreetingProps {
  name: string;
}

function Greeting({ name }: GreetingProps) {
  console.log('Greeting rendered for:', name);
  return <h2>Hello, {name}!</h2>;
}

export default Greeting;

Now update src/App.tsx to use this component:

import { useState } from 'react'
import Greeting from './Greeting'
import './App.css'

function App() {
  const [count, setCount] = useState(0)
  const [name, setName] = useState('World')

  console.log('App rendered with count:', count)

  return (
    <div className="App">
      <h1>My First React App</h1>

      <Greeting name={name} />

      <input 
        type="text" 
        value={name}
        onChange={(e) => setName(e.target.value)}
        placeholder="Enter your name"
      />

      <p>This is a counter. Click the button to increment it.</p>
      <button onClick={() => setCount(count + 1)}>
        Count: {count}
      </button>
      <button onClick={() => setCount(0)}>
        Reset
      </button>
    </div>
  )
}

export default App

Save and check your browser. You should see:

  1. A greeting: "Hello, World!"
  2. An input field: Type your name and watch the greeting update
  3. The counter: Still works as before

Now check your diagnostic tools:

Console Verification

Open the Console tab. You should see:

App rendered with count: 0
Greeting rendered for: World

Type in the input field. You'll see:

App rendered with count: 0
Greeting rendered for: W
App rendered with count: 0
Greeting rendered for: Wo
App rendered with count: 0
Greeting rendered for: Wor

Notice: Both App and Greeting re-render on every keystroke. This is normal React behavior. We'll learn to optimize this in Chapter 25.

React DevTools Verification

Open the ⚛️ Components tab. You should see:

â–Ľ App
  â–Ľ Greeting
      <h2>
  <input>
  <p>
  <button>
  <button>

Click on App. In the right panel, you'll see:

Hooks: - State: 0 (count) - State: "World" (name)

Click on Greeting. You'll see:

Props: - name: "World"

Type in the input field and watch the props update in real-time.

TypeScript Verification

Let's verify TypeScript is working. Try passing the wrong type to Greeting:

<Greeting name={123} />

Save the file. You should see a red squiggly line under 123 in your editor, and this error in the terminal:

src/App.tsx:15:23 - error TS2322: Type 'number' is not assignable to type 'string'.

15       <Greeting name={123} />
                         ~~~

This is TypeScript working correctly. It caught a type error before you even ran the code. Change it back to a string:

<Greeting name={name} />

The error disappears. This is why we use TypeScript—it catches bugs at compile time instead of runtime.

Common Setup Issues

If something isn't working, check these common issues:

Issue: Port 5173 is already in use

Error:

Port 5173 is in use, trying another one...

Solution: Either: 1. Stop the other process using port 5173 2. Let Vite use a different port (it will automatically try 5174, 5175, etc.)

Issue: Module not found

Error:

Failed to resolve import "./Greeting" from "src/App.tsx"

Solution: Check: 1. File exists at the correct path 2. File extension is included in import (.tsx) 3. File name matches exactly (case-sensitive)

Issue: React DevTools not showing

Problem: No ⚛️ tabs in DevTools

Solution: 1. Verify the extension is installed 2. Refresh the page 3. Check that you're on localhost:5173 (not file://) 4. Try restarting the browser

Issue: TypeScript errors in editor but code runs

Problem: Red squiggles in VS Code but npm run dev works

Solution: This is normal. TypeScript in your editor and TypeScript in Vite are separate. The editor might be using a different TypeScript version. To fix: 1. Open VS Code command palette (Cmd/Ctrl + Shift + P) 2. Type "TypeScript: Select TypeScript Version" 3. Choose "Use Workspace Version"

Project Scripts

Your package.json includes these scripts:

{
  "scripts": {
    "dev": "vite",
    "build": "tsc && vite build",
    "preview": "vite preview"
  }
}

Try building for production:

npm run build

You'll see output like:

vite v5.0.0 building for production...
âś“ 34 modules transformed.
dist/index.html                   0.46 kB │ gzip:  0.30 kB
dist/assets/index-a3b4c5d6.css    1.23 kB │ gzip:  0.64 kB
dist/assets/index-d7e8f9g0.js   143.21 kB │ gzip: 46.12 kB
âś“ built in 1.23s

This creates a dist/ folder with optimized files ready for deployment. We'll cover deployment in Chapter 22.

What You've Learned

You now have:

  1. A working React development environment with Vite
  2. Your diagnostic toolkit set up and verified:
  3. Browser Console for errors and logs
  4. React DevTools for component inspection
  5. Network tab for HTTP debugging
  6. TypeScript for compile-time error checking
  7. A basic understanding of:
  8. Project structure
  9. Component files
  10. State and props
  11. How React re-renders

Next Steps

In Chapter 2, we'll dive deep into components and props. You'll learn: - What components actually are - How to pass data between components - Composition patterns - Thinking in React

But before we move on, let's establish one more critical habit: reading error messages.

Learning to Read Error Messages

The most important skill in React development isn't writing code—it's reading error messages. Let's practice.

Create an intentional error. In src/App.tsx, try to use a variable that doesn't exist:

function App() {
  const [count, setCount] = useState(0)

  console.log(nonExistentVariable) // This will error

  return (
    // ... rest of component
  )
}

Save the file. Check your browser console:

Uncaught ReferenceError: nonExistentVariable is not defined
    at App (App.tsx:8:15)
    at renderWithHooks (react-dom.development.js:16305:18)
    at mountIndeterminateComponent (react-dom.development.js:20074:13)

Let's parse this error systematically:

  1. Error type: ReferenceError - trying to use an undefined variable
  2. Error message: nonExistentVariable is not defined - tells you exactly what's wrong
  3. Location: at App (App.tsx:8:15) - file, line, and column
  4. Stack trace: Shows the call chain that led to the error

This is the pattern you'll follow for every error: 1. Read the error type 2. Read the error message 3. Find the location 4. Understand the context

Remove the error line and save. The error disappears.

One More Error Pattern

Try this error:

function App() {
  const [count, setCount] = useState(0)

  return (
    <div className="App">
      <h1>My First React App</h1>
      <button onClick={() => setCount(count + 1)}>
        Count: {count}
      </button>
      <button onClick={() => setCount(0)}>
        Reset
      </button>
    // Missing closing </div>
  )
}

Save the file. You'll see a TypeScript error in your terminal:

src/App.tsx:15:3 - error TS1005: ')' expected.

15   )
     ~

And a Vite error in your browser:

[plugin:vite:react-babel] Expected corresponding JSX closing tag for <div>

Notice: You got TWO errors: 1. TypeScript caught the syntax error 2. Vite's JSX parser also caught it

This is normal. When you have a syntax error, you'll often see multiple error messages. Always fix the first error first—subsequent errors are often caused by the first one.

Add the closing </div> and save. Both errors disappear.

The Debugging Mindset

As you work through this book, you'll encounter many intentional errors. This is by design. Learning to debug is more valuable than learning to write perfect code.

When you see an error: 1. Don't panic: Errors are data, not failure 2. Read the message: It usually tells you exactly what's wrong 3. Check the location: Go to the file and line number 4. Use your tools: Console, React DevTools, Network tab 5. Isolate the problem: Comment out code until the error disappears 6. Fix incrementally: Make one change at a time

We'll practice this pattern in every chapter.

Your First React Project is Complete

You now have: - A working React development environment - Your diagnostic toolkit configured - A basic understanding of React's rendering model - The ability to read and fix errors

In Chapter 2, we'll build a real component: a User Profile Dashboard. You'll learn component composition, props, and how to think in React.

But first, take a moment to appreciate what you've accomplished. You went from zero to a working React application with TypeScript, hot module replacement, and professional debugging tools. That's not trivial.

Save your work:

git init
git add .
git commit -m "Initial React setup with Vite"

Now you're ready to learn React properly.